package springfox.documentation.oas.web;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.plugin.core.PluginRegistry;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.Constructor;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;

import io.swagger.v3.oas.models.OpenAPI;
import jakarta.servlet.http.HttpServletRequest;
import springfox.documentation.annotations.ApiIgnore;
import springfox.documentation.oas.mappers.ServiceModelToOpenApiMapper;
import springfox.documentation.oas.mappers.ServiceModelToOpenApiMapperImpl;
import springfox.documentation.oas.web.OpenApiControllerWebMvc;
import springfox.documentation.oas.web.OpenApiTransformationContext;
import springfox.documentation.oas.web.WebMvcBasePathAndHostnameTransformationFilter;
import springfox.documentation.oas.web.WebMvcOpenApiTransformationFilter;
import springfox.documentation.service.Documentation;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.DocumentationCache;
import springfox.documentation.spring.web.json.Json;
import springfox.documentation.spring.web.json.JsonSerializer;

/**
 * Pengwei
 * 2022/7/23 16:55
 */
@ApiIgnore
@RestController
@RequestMapping({"${springfox.documentation.open-api.v3.path:/v3/api-docs}"})
public class OpenApiControllerWebMvc {
    private final DocumentationCache documentationCache;
    private final ServiceModelToOpenApiMapper mapper;
    private final JsonSerializer jsonSerializer;
    private final PluginRegistry<WebMvcOpenApiTransformationFilter, DocumentationType> transformations;

    @Autowired
    public OpenApiControllerWebMvc(DocumentationCache documentationCache, ServiceModelToOpenApiMapper mapper, JsonSerializer jsonSerializer,
                        @Qualifier("webMvcOpenApiTransformationFilterRegistry") PluginRegistry<WebMvcOpenApiTransformationFilter, DocumentationType> transformations) {
        this.documentationCache = documentationCache;
        this.mapper = mapper;
        this.jsonSerializer = jsonSerializer;
        this.transformations = transformations;
    }

    @GetMapping(
            produces = {"application/json", "application/hal+json"}
    )
    public ResponseEntity<Json> getDocumentation(@RequestParam(value = "group", required = false) String group, HttpServletRequest servletRequest) {
        String groupName = (String) Optional.ofNullable(group).orElse("default");
        Documentation documentation = this.documentationCache.documentationByGroup(groupName);
        if (documentation == null) {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }
        String contextPath = servletRequest.getContextPath();

        documentation.getApiListings().values().forEach(apiListings -> {
            apiListings.forEach(apiListing -> {
                apiListing.getApis().forEach(apiDescription -> {
                    String path = apiDescription.getPath();
                    if (!path.startsWith(contextPath)) {
                        apiDescription.setPath(contextPath + apiDescription.getPath());
                    }
                });
            });
        });
        OpenAPI oas = mapper.mapDocumentation(documentation);
        OpenApiTransformationContext<HttpServletRequest> context
                = new OpenApiTransformationContext(oas, servletRequest);
        List<WebMvcOpenApiTransformationFilter> filters = transformations.getPluginsFor(DocumentationType.OAS_30);
        for (WebMvcOpenApiTransformationFilter each : filters) {
            context = context.next(each.transform(context));
        }
        return new ResponseEntity<>(jsonSerializer.toJson(oas), HttpStatus.OK);
    }
}
